光照模型的原理和实现思路

您所在的位置:网站首页 unity 屏幕空间uv 光照模型的原理和实现思路

光照模型的原理和实现思路

2023-04-12 21:49| 来源: 网络整理| 查看: 265

Phong光照模型

之前说过,游戏场景中计算光照时,需要考虑光源、模型材质和观察方向三方面的因素。很多物理学家、数学家根据这些因素,再结合真实物理世界光照效果的经验,总结出几种非常经典的光照模型,比如说兰伯特(Lambert)、环境光(Ambient)、Phong、Blinn-Phong、PBR等等,其中最经典的要数Phong光照模型。

Phong光照模型是由著名学者裴祥风在1975年提出的第一个有巨大影响力的光照计算模型。这个模型只考虑物体对直接光照的反射作用,认为环境光是常量,没有考虑物体之间相互的反射光,物体间的反射光只用环境光表示。用一个简单的公式来表示,就是:

光照 = 漫反射(Diffuse) + 高光反射(Specular) + 环境光(Ambient)

漫反射

漫反射用来描述,当光线从光源照射到模型表面时,该表面会向每个方向散射多少辐射量。**漫反射光线的强度与物体表面的法线和光源方向之间夹角的余弦值成正比。**这部分可以通过光源照射方向和法线方向的点积来实现。漫反射光的计算公式可以写成:

漫反射计算

其中,normalDir表示法线方向,lightDir表示光源方向,为了防止点积为负数,因此使用max函数将其截取到0,这样可以防止物体被后面来的光源照亮。C-light表示光源颜色,m-light表示材质的漫反射颜色。

法线方向可以通过下面的公式计算得出:

// v表示顶点 normalDir = normalize(mul(float4(v.normal,0.0),unity_WorldToObject).xyz);

光源方向通过使用Unity内置的变量_WorldSpaceLightPos0计算获取,不过这里涉及到光源类型问题,如果是平行光源,直接将_WorldSpaceLightPos0变量归一化,就能得到光源方向;但如果是点光源,还需要再减去顶点的世界空间坐标位置,然后再归一化获取光源方向。

//i.pos_world = mul(unity_ObjectToWorld,v.vertex).xyz; // v表示顶点 // 获取平行光、点光源方向 #if defined (DIRECTIONAL) half3 light_dir = normalize(_WorldSpaceLightPos0.xyz); #elif defined (POINT) half3 light_dir = normalize(_WorldSpaceLightPos0.xyz - i.pos_world); #endif

除了使用max()函数截取点积结果,使用saturate()函数也可以达到同样的效果。

接下来是C-light光源颜色,光源颜色使用内置变量_LightColor0获取,不过在此之前,还需要引入内置文件“AutoLight.cginc”

最后是M-light材质颜色,直接可以通过材质纹理获取。

知道了上述参数的计算或获取方法,就能计算出漫反射颜色了,不过还有一个问题没有考虑,那就是光的衰减。

平行光通常是用来模拟自然界的太阳光,因为太阳光有很高的强度,光的衰减几乎可以忽略不计。而对于点光来说,就必须通过计算物体到光源之间的距离来获得。具体计算方法如下:

// 计算衰减 #if defined (DIRECTIONAL) half attuenation = 1.0; // 衰减系数 #elif defined (POINT) // 计算点光源到物体之间的具体 // i.pos_world = mul(unity_ObjectToWorld,v.vertex).xyz; // v表示顶点 half distance = length(_WorldSpaceLightPos0.xyz - i.pos_world); // 衰减范围值 half range = 1.0 / unity_WorldToLight[0][0]; float attuenation = normalize((range - distance)/range); // 点光源的衰减值 #endif

漫反射最终的计算公式为:

half4 base_color = tex2D(_MainTex, i.uv); half3 NdotL = dot(normal_dir,light_dir); // 计算漫反射颜色 half3 diffuse_color = max(0.0, NdotL) * _LightColor0.xyz * base_color.xyz * attuenation;高光反射

这里的高光反射是一种经验模型,并不完全符合真实物理世界中的高光反射现象。当一束光线照射到物体上时,光线会沿着物体表面法线防线朝着和入射方向相反的方向反射,这种反射的光线,可以让物体看起来更加有光泽。

另外,高光反射还和摄像机的观察方向有关,从不同的角度观察物体,物体的高光反射也是不一样的,高光反射的计算公式是:

高光反射的计算

reflectDir表示反射方向,viewDir表示摄像机的观察方向,C-light表示光源颜色。

反射方向使用reflect()函数,传入光源负方向和法线方向,具体代码如下:

half3 reflect_dir = reflect(-light_dir, normal_dir);

观察方向通过摄像机世界空间坐标位置减去顶点世界空间位置计算得出,具体代码如下:

half3 view_dir = normalize(_WorldSpaceCameraPos.xyz - i.pos_world);

高光反射的计算代码如下:

half3 view_dir = normalize(_WorldSpaceCameraPos.xyz - i.pos_world); half3 reflect_dir = reflect(-light_dir, normal_dir); half RdotV = dot(reflect_dir,view_dir); half3 spec_color = max(0.0,RdotV) * _LightColor0.xyz;

这个高光计算的代码还是属于简单版的,大家可以引入其他变量,比如反光度(shininess)、高光强度(specIntensity)、高光遮罩(specMask)等等,灵活控制高光反射的效果。

half3 spec_color = pow(max(0.0,RdotV),_Shininess) * _LightColor0.xyz * _SpecIntensity * spec_mask.rgb;高光反射环境光

最后一步的添加环境光,环境光相对来说就非常简单了,可以使用纯色,或者使用一张纹理贴图来制作。

Phong光照模型最后的计算结果是

half3 phong_color = diffuse_color + spec_color + _AmbientColor.xyz;总结

上述漫反射、高光反射和环境光的计算属于简易版本,实际在编写shader中,还可以加入更多参数实现更复杂精确的控制效果,比如说混入颜色贴图、粗糙图贴图、法线贴图。

另外,需要提醒的是:任何光照模型的计算,都需要根据渲染路径的思路来进行计算,具体思路请参考Unity中的渲染路径RenderingPath,下面给出一个光照模型shader Pass的模板:

Shader "lit/Phong" { SubShader { Tags {"RenderType"="Opaque"} Pass { Tags { "LightMode"="ForwardBase" } CGPROGRAM #pragma multi_compile_fwdbase #include "AutoLight.cginc" } Pass { Tags { "LightMode"="ForwardAdd" } Blend One One CGPROGRAM #pragma multi_compile_fwdadd #include "AutoLight.cginc" ENDCG } } }

以前向渲染路径为例,它使用到了两个Pass,一个是ForwardBase,另外一个是ForwardAdd,因此在Tags 中要将这两个Pass名称传入到LightMode。

初次之外,还需要分别添加multi_compile_fwdbase 和 multi_compile_fwdadd。

另外,还需要再引入AutoLight.cginc内置文件,这个文件包含了许多光源计算的内置变量。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3